/*
 * @Descripttion:
 * @version: 1.0.0
 * @Date: 2021-05-19 15:15:45
 * @LastEditTime: 2022-06-22 10:52:13
 */

import type {
  Cartesian2,
  Viewer
} from "cesium"
import type { IMouseHandlerCallback } from "../CustomCesium"
import {
  createGuid,
  defined,
  ScreenSpaceEventHandler,
  ScreenSpaceEventType
} from "cesium"

interface IEventHandler {
  // 内部唯一索引
  readonly guid: string
  // 外部唯一索引
  key: string
  // 事件处理器
  handler: ScreenSpaceEventHandler
}

// 用于 区分单双击
interface IClickTimer {
  // 同步处理器索引
  readonly guid: string
  // 计时器ID
  timer: number
}

/**
 * @descripttion: 监听管理器
 */
class MouseHandler {
  private viewer: Viewer
  private readonly handlers: IEventHandler[]
  private readonly clickTimers: IClickTimer[]

  /**
   * @descripttion:
   * @param {Viewer} viewer 视图
   */
  constructor(viewer: Viewer) {
    this.viewer = this.init(viewer)
    this.handlers = []
    this.clickTimers = []
  }

  /**
   * @descripttion: 初始化
   * @param {Viewer} viewer 视图
   * @return {Viewer} 视图
   */
  private init(viewer: Viewer) {
    if (!defined(viewer))
      throw new Error("MouseEventListener:Viewer,无效的视图对象！")
    return viewer
  }

  /**
   * @descripttion: 添加事件监听
   * @param {string} key 事件监听标识
   * @param {ScreenSpaceEventHandler} handler 事件监听对象
   * @return {object} 添加结果
   */
  private add(key = "", handler: ScreenSpaceEventHandler) {
    if (this.isExists(key) && !handler) return undefined
    const guid = createGuid()
    key = key || guid
    this.handlers.push({ guid, key, handler })
    return guid
  }

  /**
   * @descripttion: 获取事件监听
   * @param {string} key 标识
   * @return {object|undefined} 返回结果
   */
  private get(key = "") {
    if (!key) return undefined
    const filterArr = this.handlers.filter(
      item =>
        item.key === key
        || item.key === `${key}wheel`
        || item.key === `${key}leftClick`
        || item.key === `${key}leftDoubelClick`
        || item.key === `${key}rightClick`
        || item.key === `${key}leftDown`
        || item.key === `${key}leftUp`
    )
    return filterArr.length === 1 || filterArr.length === 6
      ? filterArr
      : undefined
  }

  /**
   * @descripttion: 添加计时器
   * @param {string} guid 唯一索引
   * @param {object} timer 计时器
   * @return {boolean} 结果
   */
  private addClickTimer(guid = "", timer: NodeJS.Timeout) {
    if (!guid || timer) return false
    const isExists
      = this.clickTimers.findIndex(item => item.guid === guid) >= 0
    if (!isExists) {
      this.clickTimers.push({ guid, timer })
      return true
    }
    return false
  }

  /**
   * @descripttion: 根据guid获取计时器
   * @param {string} guid 唯一索引
   * @return {*} 结果
   */
  private getClickTimer = (guid = "") => {
    if (!guid) return undefined
    const filterArr = this.clickTimers.filter(item => item.guid === guid)
    return filterArr.length === 1 ? filterArr[0] : undefined
  }

  /**
   * @descripttion: 根据guid清除计时器
   * @param {string} guid 唯一索引
   * @return {*} 结果
   */
  private clearClickTimer = (guid = "") => {
    const clickTimer = this.getClickTimer(guid)
    let result = false
    if (clickTimer) {
      clearTimeout(clickTimer.timer)
      this.clickTimers.splice(
        this.clickTimers.findIndex(c => c.guid == guid),
        1
      )
      result = true
    }
    return result
  }

  /**
   * @descripttion: 判断是否存在该监听
   * @param {string} key 外部唯一标识
   * @return {Index | undefined} 索引值或undefined
   */
  isExists(key = "") {
    if (!key) return true
    return this.handlers.findIndex(item => item.key === key) >= 0
  }

  /**
   * @descripttion: 添加监听鼠标滚轮事件
   * @param {string} key 标识标
   * @param {Function} callback
   * @return {string|undefined} 监听对象或者undefined(未创建成功)
   */
  addMouseWheelHandler(
    key = "",
    callback: (res: IMouseHandlerCallback) => void
  ): string | undefined {
    // 判断是否已存在
    // 可以为空，后续创建监听时会自动赋值
    if (this.isExists(key)) return undefined

    // 创建handler
    const mouseWheelHandler = new ScreenSpaceEventHandler(
      this.viewer.scene.canvas
    )
    // 将handler放入缓存管理
    const guid = this.add(key, mouseWheelHandler)

    // 未创建成功，返回
    if (!guid) return undefined

    // 为handler指定监听类型
    mouseWheelHandler.setInputAction((mouseWheel: any) => {
      callback({ mouseWheelPosition: mouseWheel })
    }, ScreenSpaceEventType.WHEEL)

    // 返回监听对象索引
    return guid
  }

  /**
   * @descripttion: 添加鼠标左键单机监听
   * @param {string} key 索引
   * @param {Function} callback 回调函数
   * @return {string|undefined} 监听对象或者undefined
   */
  addLeftMouseClickHandler(
    key = "",
    callback: (res: IMouseHandlerCallback) => void
  ): string | undefined {
    // 判断是否已存在
    // 可以为空，后续创建监听时会自动赋值
    if (this.isExists(key)) return undefined

    // 创建handler
    const leftMouseClickHandler = new ScreenSpaceEventHandler(
      this.viewer.scene.canvas
    )
    // 将handler放入缓存管理
    const guid = this.add(key, leftMouseClickHandler)

    // 未创建成功，返回
    if (!guid) return undefined

    // 为handler指定监听类型
    leftMouseClickHandler.setInputAction((movement: { position: Cartesian2 }) => {
      try {
        // 清除计时器
        this.clearClickTimer(guid)

        // 点击事件延时200ms执行
        // 区分鼠标双击
        const clickTimer = setTimeout(() => {
          let screenPosition: Cartesian2 = movement.position

          if (defined(screenPosition)) screenPosition = screenPosition.clone()

          if (callback && typeof callback === "function") {
            // 返回结果
            callback({
              screenPosition
            })
          }
        }, 200)

        // 添加计时器
        this.addClickTimer(guid, clickTimer)
      } catch (error) {
        console.error(new Error(`MouseLeftClickHandler: ${error}`))
      }
    }, ScreenSpaceEventType.LEFT_CLICK)

    // 返回监听对象索引
    return guid
  }

  /**
   * @descripttion: 添加鼠标左键单机监听
   * @param {string} key 索引
   * @param {Function} callback 回调函数
   * @return {object|undefined} 监听对象或者undefined
   */
  addLeftMouseDoubleClickHandler(
    key = "",
    callback: (res: IMouseHandlerCallback) => void
  ): string | undefined {
    // 判断是否已存在
    // 可以为空，后续创建监听时会自动赋值
    if (this.isExists(key)) return undefined

    // 创建handler
    const leftMouseDoubleClickHandler = new ScreenSpaceEventHandler(
      this.viewer.scene.canvas
    )
    // 将handler放入缓存管理
    const guid = this.add(key, leftMouseDoubleClickHandler)

    // 未创建成功，返回
    if (!guid) return undefined

    // 为handler指定监听类型
    leftMouseDoubleClickHandler.setInputAction((movement: { position: Cartesian2 }) => {
      try {
        // 屏蔽鼠标单击
        this.clickTimers.forEach((t) => {
          this.clearClickTimer(t.guid)
        })

        let screenPosition: Cartesian2 = movement.position

        if (defined(screenPosition)) screenPosition = screenPosition.clone()

        if (callback && typeof callback === "function") {
          // 返回结果
          callback({
            screenPosition
          })
        }
      } catch (error) {
        console.error(new Error(`MouseLeftDoubleClickHandler: ${error}`))
      }
    }, ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
    // 返回监听对象索引
    return guid ? key || guid : undefined
  }

  /**
   * @descripttion: 添加鼠标左键单机监听
   * @param {string} key 索引
   * @param {Function} callback 回调函数
   * @return {object|undefined} 监听对象或者undefined
   */
  addRightMouseClickHandler(
    key = "",
    callback: (res: IMouseHandlerCallback) => void
  ) {
    // 判断是否已存在
    // 可以为空，后续创建监听时会自动赋值
    if (this.isExists(key)) return undefined

    // 创建handler
    const rightMouseClickHandler = new ScreenSpaceEventHandler(
      this.viewer.scene.canvas
    )
    // 将handler放入缓存管理
    const guid = this.add(key, rightMouseClickHandler)

    // 未创建成功，返回
    if (!guid) return undefined

    // 为handler指定监听类型
    rightMouseClickHandler.setInputAction((movement: { position: Cartesian2 }) => {
      try {
        let screenPosition: Cartesian2 = movement.position

        if (defined(screenPosition)) screenPosition = screenPosition.clone()

        if (callback && typeof callback === "function") {
          // 返回结果
          callback({
            screenPosition
          })
        }
      } catch (error) {
        console.error(new Error(`MouseRightClickHandler: ${error}`))
      }
    }, ScreenSpaceEventType.RIGHT_CLICK)
    // 返回监听对象索引
    return guid
  }

  /**
   * @descripttion: 添加鼠标左键按下监听
   * @param {string} key 索引
   * @param {Function} callback 回调函数
   * @return {object|undefined} 监听对象或者undefined
   * @author: ssr
   */
  addLeftMouseDownHandler(
    key = "",
    callback: (res: IMouseHandlerCallback) => void
  ) {
    // 判断是否已存在
    // 可以为空，后续创建监听时会自动赋值
    if (this.isExists(key)) return undefined

    // 创建handler
    const leftMouseDownHandler = new ScreenSpaceEventHandler(
      this.viewer.scene.canvas
    )
    // 将handler放入缓存管理
    const guid = this.add(key, leftMouseDownHandler)

    // 未创建成功，返回
    if (!guid) return undefined

    // 为handler指定监听类型
    leftMouseDownHandler.setInputAction((movement: { position: Cartesian2 }) => {
      try {
        let screenPosition: Cartesian2 = movement.position

        if (defined(screenPosition)) screenPosition = screenPosition.clone()

        if (callback && typeof callback === "function") {
          // 返回结果
          callback({
            screenPosition
          })
        }
      } catch (error) {
        console.error(new Error(`LeftMouseDownHandler: ${error}`))
      }
    }, ScreenSpaceEventType.LEFT_DOWN)
    // 返回监听对象索引
    return guid
  }

  /**
   * @descripttion: 添加鼠标左键弹起监听
   * @param {string} key 索引
   * @param {Function} callback 回调函数
   * @return {object|undefined} 监听对象或者undefined
   * @author: ssr
   */
  addLeftMouseUpHandler(
    key = "",
    callback: (res: IMouseHandlerCallback) => void
  ) {
    // 判断是否已存在
    // 可以为空，后续创建监听时会自动赋值
    if (this.isExists(key)) return undefined

    // 创建handler
    const leftMouseUpHandler = new ScreenSpaceEventHandler(
      this.viewer.scene.canvas
    )
    // 将handler放入缓存管理
    const guid = this.add(key, leftMouseUpHandler)

    // 未创建成功，返回
    if (!guid) return undefined

    // 为handler指定监听类型
    leftMouseUpHandler.setInputAction((movement: { position: Cartesian2 }) => {
      try {
        let screenPosition: Cartesian2 = movement.position

        if (defined(screenPosition)) screenPosition = screenPosition.clone()

        if (callback && typeof callback === "function") {
          // 返回结果
          callback({
            screenPosition
          })
        }
      } catch (error) {
        console.error(new Error(`LeftMouseUpHandler: ${error}`))
      }
    }, ScreenSpaceEventType.LEFT_UP)
    // 返回监听对象索引
    return guid
  }

  /**
   * @descripttion: 添加鼠标左键单机监听
   * @param {string} key 索引
   * @param {Function} callback 回调函数
   * @return {*} 唯一索引
   */
  addMouseClickHandler(
    key = "",
    callback: (res: IMouseHandlerCallback) => void
  ) {
    // 判断是否已存在
    if (this.isExists(key)) return undefined
    // 创建处理器索引
    key = key || createGuid()
    const keys = [
      `${key}wheel`,
      `${key}leftClick`,
      `${key}leftDoubelClick`,
      `${key}rightClick`,
      `${key}leftDown`,
      `${key}leftUp`
    ]

    // 创建鼠标事件
    this.addMouseWheelHandler(keys[0], callback)
    this.addLeftMouseClickHandler(keys[1], callback)
    this.addLeftMouseDoubleClickHandler(keys[2], callback)
    this.addRightMouseClickHandler(keys[3], callback)
    this.addLeftMouseDownHandler(keys[4], callback)
    this.addLeftMouseUpHandler(keys[5], callback)

    // 返回监听对象索引
    return key
  }

  /**
   * @descripttion: 监听鼠标移动事件获取坐标信息
   * @param {string} key 索引
   * @param {Function} callback 回调函数
   * @return {*}
   */
  addMouseMoveHandler(
    key = "",
    callback: (res: IMouseHandlerCallback) => void
  ) {
    // 判断是否已存在
    // 可以为空，后续创建监听时会自动赋值
    if (this.isExists(key)) return undefined

    // 创建handler
    const mouseMoveHandler = new ScreenSpaceEventHandler(
      this.viewer.scene.canvas
    )
    // 将handler放入缓存管理
    const guid = this.add(key, mouseMoveHandler)

    // 未创建成功，返回
    if (!guid) return undefined

    // 为handler指定监听类型
    mouseMoveHandler.setInputAction((movement: { endPosition: Cartesian2 }) => {
      try {
        let screenPosition: Cartesian2 = movement.endPosition

        if (defined(screenPosition)) screenPosition = screenPosition.clone()

        if (callback && typeof callback === "function") {
          // 返回结果
          callback({
            screenPosition
          })
        }
      } catch (error) {
        console.error(new Error(`MouseMoveHandler: ${error}`))
      }
    }, ScreenSpaceEventType.MOUSE_MOVE)
    // 返回监听对象索引
    return guid
  }

  /**
   * @descripttion: 销毁处理器
   * @param {ScreenSpaceEventHandler} handler 处理器
   * @return {result | boolean} 处理结果
   */
  private destroyHandler = (handler: ScreenSpaceEventHandler) => {
    let result = false
    if (defined(handler)) {
      if (!handler.isDestroyed()) {
        handler.destroy()
        result = true
      }
    }
    return result
  }

  /**
   * @descripttion: 销毁处理器(byKey)
   * @param {string} key 索引
   * @return {*} 结果
   */
  destroyHandlerByKey(key: string) {
    let result = false
    // 获取处理器
    const handlerArr = this.get(key)
    // 判断处理器有无
    if (handlerArr) {
      let index = 0
      // 删除处理器
      handlerArr.forEach((obj) => {
        const { guid, handler } = obj
        if (this.destroyHandler(handler)) {
          this.handlers.splice(
            this.handlers.findIndex(h => h.guid == guid),
            1
          )
          index++
        }
      })
      index === handlerArr.length && (result = true)
    }
    return result
  }
}
export default MouseHandler
